package stud.mpgame.network;

import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.TypedDictionary;

import stud.mpgame.network.MessageDescription;
import stud.mpgame.network.UserObject;
import stud.mpgame.network.NetworkMonitor;

/**
 * ...
 * @author Timofeev
 */

class Message {
    
    private var messageDescription:MessageDescription;
    private var serialNumber:Int;
    private var sentTime:Int;
    private var receivedTime:Int;
    private var protocolVersion:Int;
    private var networkMonitor:NetworkMonitor;
    private var body:IntHash< UserObject >;        
    
    public function new(networkMonitor:NetworkMonitor, messageDescription:MessageDescription) {
        this.body = new IntHash< UserObject >();
        this.networkMonitor = networkMonitor;
        this.messageDescription = messageDescription;
    }
    
    public function getMessageDescription():MessageDescription {
        return this.messageDescription;
    }
    
    public function getNetworkMonitor():NetworkMonitor {
        return this.networkMonitor;
    }

    public function getSerialNumber():Int {
        return this.serialNumber;
    }

    public function getSentTime():Int {
        return this.sentTime;
    }
    
    public function getReceivedTime():Int {
        return this.receivedTime;
    }
    
    public function getProtocolVersion():Int {
        return this.protocolVersion;
    }
    
    public function getUserObjectById(id:Int):UserObject {
        return body.get(id);
    }
    
    public function getUserObject(userObjectClass:Class<Dynamic>):UserObject {
        return body.get(messageDescription.getUserObjectId(userObjectClass));
    }
    
    public function putUserObject(userObject:UserObject):Void {
        body.set(messageDescription.getUserObjectId(Type.getClass(userObject)), userObject);        
    }

    public function removeUserObject(userObject:UserObject):Void {
        body.remove(messageDescription.getUserObjectId(Type.getClass(userObject)));
    }    
    
    public function writeExternal(output:IDataOutput, serialNumber:Int, sentTime:Int, protocolVersion:Int):Void {
        output.writeInt(messageDescription.getID());        
        this.serialNumber = serialNumber;
        output.writeInt(serialNumber);         
        this.sentTime = sentTime;
        output.writeInt(sentTime);        
        this.protocolVersion = protocolVersion;
        output.writeInt(protocolVersion);
        var identifiers:TypedDictionary< Int, Class< Dynamic > > = messageDescription.getIdentifiers();
        for (id in identifiers) {            
            output.writeInt(id);
            var userObject:UserObject = this.getUserObjectById(id);
            userObject.writeExternal(output, this);
        }
    }
    
    public function readExternal(input:IDataInput, receivedTime:Int):Void {
        var descriptionID:Int = input.readInt();
        this.messageDescription = MessageDescription.getMessageDescriptionByID(descriptionID);
        this.serialNumber = input.readInt();
        this.sentTime = input.readInt();
        this.receivedTime = receivedTime;
        this.protocolVersion = input.readInt();        
        for (i in messageDescription.getIdentifiers()) {
            var userObjectId:Int = input.readInt();
            var userObject:UserObject = messageDescription.createUserObject(userObjectId);
            userObject.readExternal(input, this);
            this.putUserObject(userObject);
        }
    }
    
    public function send():Void {
        networkMonitor.encode(this);                
    }       
    
}